﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

//参考自UINet.h的定义	@version  2014.3.6.1

namespace UISockets
{
    class CTIMsg
    {
        #region 相关资料和说明
        /* 
         * 下面是C++与C#之间数据转换以及大小，等号前面是C++类型，等号后面是C#类型
         * short=short 2byte
         * int=int 4Bytes
         * long=int 4Bytes
         * bool =bool 1Byte 
         * char=byte  1Byte 
         * wchar_t = char 2Bytes
         * float=float 4Bytes
         * double=double 8Bytes
         */
        #endregion

        #region 基本数据结构体定义
        //所有的请求和回复的消息头 都只能是这个，通过它获取大小和消息ID， 决定下一步的消息处理
        public struct PACK_HEAD
        {
            //这里用于得到PACK_HEAD结构体的大小， 事实上等于8, 只是为了增加代码的阅读性，static的数据结构不会占用struct的大小
            public static readonly int packHeadSize = Marshal.SizeOf(typeof(CTIMsg.PACK_HEAD));

            public uint size;
            public uint id;
        }

        //认证KEY
        public struct GUID
        {
            public GUID(uint d1, ushort d2, ushort d3, byte d4_0, byte d4_1, byte d4_2, byte d4_3, byte d4_4, byte d4_5, byte d4_6, byte d4_7)
            {
                this.Data1 = d1;
                this.Data2 = d2;
                this.Data3 = d3;
                Data4 = new byte[8] { d4_0, d4_1, d4_2, d4_3, d4_4, d4_5, d4_6, d4_7 };
            }
            public uint Data1;
            public ushort Data2;
            public ushort Data3;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            public byte[] Data4;
        }

        //这里的GUI必须固定成这个样子, CTI只会将这个GUID当做是UI的连接
        public static readonly GUID GUID_KEY_UICOMMAND = new GUID(0x8cd45b17, 0x588a, 0x4849, 0x92, 0x28, 0x5, 0x3c, 0x8f, 0xbb, 0x8, 0xb8);

        //几个C++字符串的长度定义
        public const int MAX_ERROR_STR = 256;
        public const int MAX_DRIVER_NAME = 32;
        public const int MAX_PATH = 260;

        //Driver的配置信息(变长的消息结构体)
        public struct DRIVER_CONFIG
        {
            public static readonly int size = Marshal.SizeOf(typeof(CTIMsg.DRIVER_CONFIG));

            public uint structSize;

            //Driver名称
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;

            //Driver对应之DLL文件名
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)]
            public byte[] dllFileName;

            //配置信息格式
            //Key1=value1, Key2=value2, Key3=value3,
            //存储结构是一个以0结尾的char数组
            //char driverConfig[];
        };


        //单个Driver所包含的DriverInfo定义(变长的消息结构体)
        public struct DRIVER_INFO
        {
            public static readonly int size = Marshal.SizeOf(typeof(CTIMsg.DRIVER_INFO));

            public uint structSize;
            //Driver名称
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;

            //Driver对应之DLL文件名
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)]
            public byte[] dllFileName;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ERROR_STR)]
            public byte[] errStr;//为空说明操作成功，否则里面就是错误说明

            //该信息的来源是DriverDLL的GetDriverInfo接口Frame只做转发工作，不解析里面内容,获取失败返回错误说明
            //Key1(Format)=value1, Key2(Format)=value2, Key3(Format)=value3, ; Key1:State1, Key2:State2,
            //分号前面是配置信息数据(可以进行配置的数据)， 分号后面是运行状态信息（不能修改的数据)
            //Format的格式为:  <pos>,dataType<(maxSize)>,showType,<valueRange>,
            //                位置,数据类型(最大长度或者最大值),显示的类型,取值范围或者选项
            //                尖括号修饰的选项为可选项
            //
            //                dataType取值范围:char,int,bool,
            //                showType的取值范围:Ｔ表示Text控件，R表示Radio button, C表示Check Box, L表示Combo,其他参考SA
            //                valueRange格式为: value<-显示的值> 每个值之间以#号串联起来, 修改的时候需要用value进行赋值
            //                PS:取值范围之类的可以进行扩展，具体参考SA
            //
            //                举例
            //                HostMain(2,char(20),T,,)=127.0.0.1,
            //                Port(3,int,T,,)=7777,
            //                Active(4,bool,R,1-active#0-inactivity,)=1
            //
            //存储结构是一个以0结尾的char数组
            //char driverInfo[];
        };

        #endregion

        #region UI发送给CTI的消息定义

        //当去CTI连接成功之后发送的第一个认证消息包
        public struct PACK_STREAM_SOCKET_KEY
        {
            public const uint ID = unchecked((uint)-1);
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_STREAM_SOCKET_KEY));

            public PACK_HEAD head;
            public GUID key;
        }

        //请求当前所有的Driver信息
        public struct PACK_REQU_GET_DRIVER_INFO_LIST
        {
            public const uint ID = 1001;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_GET_DRIVER_INFO_LIST));

            public PACK_HEAD head;
        };

        //UI根据DriverName，获取单个Driver的信息,CTI将会调用DLL的GetDriverInfo接口 
        [Serializable]
        public struct PACK_REQU_DRIVER_INFO_BY_DRIVERNAME
        {
            public const uint ID = 1002;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_DRIVER_INFO_BY_DRIVERNAME));

            public PACK_HEAD head;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;
        };

        //UI请求单个Driver的信息
        public struct PACK_REQU_DRIVER_INFO_BY_DLLNAME
        {
            public const uint ID = 1003;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_DRIVER_INFO_BY_DLLNAME));

            public PACK_HEAD head;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)]
            public byte[] dllFileName;//DLL文件名
        };

        //UI请求增加一个Driver，由于DRIVER_CONFIG是变长的消息，因此本消息结构体也是变长
        public struct PACK_REQU_ADD_DRIVER
        {
            public const uint ID = 1004;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_ADD_DRIVER));

            public PACK_HEAD head;
            public DRIVER_CONFIG driverConfig;
        };

        //UI请求更新Driver， 配置信息是变长的
        public struct PACK_REQU_UPDATE_DRIVER
        {
            public const uint ID = 1005;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_UPDATE_DRIVER));

            public PACK_HEAD head;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;

            //配置信息格式
            //Key1=value1, Key2=value2, Key3=value3,
            //存储结构是一个以0结尾的char数组
            //char driverConfig[];
        };

        //Frame将会停止该Driver，然后进行卸载DLL，最后删除该Driver所使用的资源
        public struct PACK_REQU_DELETE_DRIVER
        {
            public const uint ID = 1006;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_DELETE_DRIVER));

            public PACK_HEAD head;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;
        };

        //UI请求启动某个Driver
        public struct PACK_REQU_START_DRIVER
        {
            public const uint ID = 1007;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_START_DRIVER));

            public PACK_HEAD head;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;
        }

        //UI请求停止某个Driver
        public struct PACK_REQU_STOP_DRIVER
        {
            public const uint ID = 1008;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_STOP_DRIVER));

            public PACK_HEAD head;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;
        }

        //UI->Frame->Controller
        //UI发送相关命令给Controller,通过Frame转发，Frame不需要知道其中的相关细节
        public struct PACK_REQU_CONTROLLER_COMMAND
        {
            public const uint ID = 1009;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_REQU_CONTROLLER_COMMAND));

            public PACK_HEAD head;
            //后面紧跟需要Controller进行解析的数据包
            //开启或者关闭话务日志
            //请求获取当前的线路信息
        };

        //UI->Controller
        //UI请求开启话务日志信息
        public struct REQU_CONTROLLER_OPEN_LOG
        {
            public const uint ID = 2001;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.REQU_CONTROLLER_OPEN_LOG));

            public PACK_HEAD head;
        };

        //UI->Controller
        //UI请求关闭话务日志命令
        public struct REQU_CONTROLLER_CLOSE_LOG
        {
            public const uint ID = 2002;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.REQU_CONTROLLER_CLOSE_LOG));

            public PACK_HEAD head;
        };

        //UI->Controller
        //UI请求开启线路信息     
        //在线路较多的情况下，该操作会影响到话务事件的处理出现延迟，可以考虑分批次发送线路信息,也需要考虑消息队列被填满的情况
        public struct REQU_OPEN_LINE_STATUS
        {
            public const uint ID = 2003;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.REQU_OPEN_LINE_STATUS));

            public PACK_HEAD head;
        };


        //UI->Controller
        //UI请求关闭线路状态信息
        public struct REQU_CLOSE_LINE_STATUS
        {
            public const uint ID = 2004;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.REQU_CLOSE_LINE_STATUS));

            public PACK_HEAD head;
        };

        #endregion

        #region CTI发送给Frame的消息定义
        //CTI收到UI的key包之后，回复这个消息给UI
        public struct PACK_RESP_CONNECT_SUCCESS
        {
            public const uint ID = 0;

            public PACK_HEAD head;
        };

        //当已经存在一个UI连接时，CTI发送给UI连接失败的消息
        public struct PACK_RESP_LINK_EXIST
        {
            public const uint ID = 1;

            public PACK_HEAD head;
        };


        //CTI回复当前所有的Driver信息
        public struct PACK_RESP_DRIVER_INFO_LIST
        {
            public const uint ID = 1101;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_DRIVER_INFO_LIST));

            public PACK_HEAD head;
            public uint driverCount;//指定driver的个数
            //后面是public struct DRIVER_INFO 的 array,array的大小取决于driverCount
        };

        //CTI回复UI单个Driver的信息
        public struct PACK_RESP_DRIVER_INFO_BY_DRIVERNAME
        {
            public const uint ID = 1102;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_DRIVER_INFO_BY_DRIVERNAME));

            public PACK_HEAD head;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ERROR_STR)]
            public byte[] errStr;//为空说明操作成功，否则里面就是错误说明

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;//driver的名称

            //后面紧跟一个char数组，格式参考DRIVER_INFO里面的DriverInfo
            //char driverInfo[]
        };

        //CTI回复UI单个Driver的信息
        public struct PACK_RESP_DRIVER_INFO_BY_DLLNAME
        {
            public const uint ID = 1103;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_DRIVER_INFO_BY_DLLNAME));

            public PACK_HEAD head;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ERROR_STR)]
            public byte[] errStr;//为空说明操作成功，否则里面就是错误说明

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)]
            public byte[] dllFileName;//DLL文件名

            //后面紧跟一个char数组，格式参考DRIVER_INFO里面的DriverInfo
            //char driverInfo[]
        };

        public struct PACK_RESP_ADD_DRIVER
        {
            public const uint ID = 1104;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_ADD_DRIVER));

            public PACK_HEAD head;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ERROR_STR)]
            public byte[] errStr;//为空说明操作成功，否则里面就是错误说明

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;//driver的名称
        };

        public struct PACK_RESP_UPDATE_DRIVER
        {
            public const uint ID = 1105;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_UPDATE_DRIVER));

            public PACK_HEAD head;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ERROR_STR)]
            public byte[] errStr;//为空说明操作成功，否则里面就是错误说明

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;//driver的名称
        };


        public struct PACK_RESP_DELETE_DRIVER
        {
            public const uint ID = 1106;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_DELETE_DRIVER));

            public PACK_HEAD head;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ERROR_STR)]
            public byte[] errStr;//为空说明操作成功，否则里面就是错误说明

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;//driver的名称
        };


        public struct PACK_RESP_START_DRIVER
        {
            public const uint ID = 1107;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_START_DRIVER));

            public PACK_HEAD head;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ERROR_STR)]
            public byte[] errStr;//为空说明操作成功，否则里面就是错误说明

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;//driver的名称
        };

        public struct PACK_RESP_STOP_DRIVER
        {
            public const uint ID = 1108;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_STOP_DRIVER));

            public PACK_HEAD head;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ERROR_STR)]
            public byte[] errStr;//为空说明操作成功，否则里面就是错误说明

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVER_NAME)]
            public byte[] driverName;//driver的名称
        };

        //Controller->Frame->UI
        //Controller对于UI的相关命令的回复，通过Frame转发，Frame不需要知道其中的相关细节
        public struct PACK_RESP_CONTROLLER_COMMAND
        {
            public const uint ID = 1109;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.PACK_RESP_CONTROLLER_COMMAND));

            public PACK_HEAD head;
            //后面紧跟Controller发送给UI的数据包
            //话务日志信息
            //监控线路信息

            //由于Controller回复给UI的消息结构一致，都是以PACK_HEAD加变长字符串的结构，为了简单起见，直接在本结构体下面附加一个PACK_HEAD
            public PACK_HEAD constoller_head;
        };

        //Controller->UI
        //Controller发送话务日志到UI
        public struct CONTROLLER_RESP_LOG
        {
            public const uint ID = 2101;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.CONTROLLER_RESP_LOG));

            public PACK_HEAD head;

            //后面紧跟一个变长的char数组,内容是话务日志信息
            //char log[];
        };

        //Controller->UI
        //关于线路信息的第一个数据包，栏位信息
        public struct CONTROLLER_RESP_LINE_STATUS_FIELDS
        {
            public const uint ID = 21031;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.CONTROLLER_RESP_LINE_STATUS_FIELDS));

            public PACK_HEAD head;

            //后面紧跟一个变长的char数组,内容是栏位Key名表
            //char log[];
        };

        //Controller->UI
        //单条线路的原始状态信息
        public struct CONTROLLER_RESP_LINE_STATUS_ADD
        {
            public const uint ID = 21032;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.CONTROLLER_RESP_LINE_STATUS_ADD));

            public PACK_HEAD head;

            //后面紧跟一个变长的char数组,内容是具体的状态信息
            //char log[];
        };

        //Controller->UI
        //单条线路状态信息改变
        public struct CONTROLLER_RESP_LINE_STATUS_CHANGE
        {
            public const uint ID = 21033;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.CONTROLLER_RESP_LINE_STATUS_CHANGE));

            public PACK_HEAD head;

            //后面紧跟一个变长的char数组,内容是具体的状态信息
            //char log[];
        };

        //Controller->UI
        //单条线路被删除,该消息暂时没有实现
        public struct CONTROLLER_RESP_LINE_STATUS_DELETE
        {
            public const uint ID = 21034;
            public static readonly int packSize = Marshal.SizeOf(typeof(CTIMsg.CONTROLLER_RESP_LINE_STATUS_DELETE));

            public PACK_HEAD head;

            //后面紧跟一个变长的char数组,内容是具体的状态信息
            //char log[];
        };

        #endregion

        #region  辅助接口函数

        public static Byte[] StructToBytes(Object structure, int extraSize= 0)
        {
            // here have some error
            Int32 size = Marshal.SizeOf(structure);
            size += extraSize;
            IntPtr buffer = Marshal.AllocHGlobal(size);
            Byte[] bytes = new Byte[size];
            Marshal.StructureToPtr(structure, buffer, true);
            Marshal.Copy(buffer, bytes, 0, size);
            Marshal.FreeHGlobal(buffer);
            return bytes;
        }

        //将一个变长的struct转变为一个bytes[]
        //最后一个元素是变长的字符串
        //本对象不管理struct中关于struct size的值,需要在传递进来之前structure内部已经有对该值进行赋值和修正
        public static Byte[] VariableLengthStructToBytes(Object structure, byte[] varString)
        {
            Byte[] bytes = StructToBytes(structure, varString.Length);
            Array.Copy(varString, 0, bytes, Marshal.SizeOf(structure), varString.Length);
            return bytes;
        }

        //将整个byte[] 转换为一个struct
        public static object BytesToStruct(byte[] bytes, Type strcutType)
        {
            int size = Marshal.SizeOf(strcutType);
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.Copy(bytes, 0, buffer, size);
                return Marshal.PtrToStructure(buffer, strcutType);
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }

        //将byte的部分转换为一个struct
        public static object BytesToStruct(byte[] bytes, int structBegin, Type strcutType)
        {
            int size = Marshal.SizeOf(strcutType);
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.Copy(bytes, structBegin, buffer, size);
                return Marshal.PtrToStructure(buffer, strcutType);
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }

        //将byte[]转变为一个变长的struct
        //该struct的第一个成员变量是uint类型，表示struct的大小,最后一个元素是变长的字符串
        //返回一个tuple，第一个是struct，第二个是变长的字符串，第三个表示变长的struct的大小
        public static Tuple<object, String, int> BytesToVariableLengthStruct(byte[] bytes, int structBegin, Type strcutType)
        {
            //读取存储变长的struct的大小的数值
            int structSizeSize = Marshal.SizeOf(typeof(uint));
            IntPtr structSizeBuffer = Marshal.AllocHGlobal(structSizeSize);
            int structSize = 0;
            try
            {
                Marshal.Copy(bytes, structBegin, structSizeBuffer, structSizeSize);
                structSize = (int)Marshal.PtrToStructure<uint>(structSizeBuffer);
            }
            finally
            {
                Marshal.FreeHGlobal(structSizeBuffer);
            }
            
            //读取整个struct
            int structObjectSize = Marshal.SizeOf(strcutType);
            object structObject = BytesToStruct(bytes, structBegin, strcutType);

            //读取变长的字符串
            int varStrBegin = structBegin + structObjectSize;
            int varStrSize = structSize - structObjectSize;
            String resultString = NulltTerminatedAsciiBytesToString(bytes, varStrBegin, varStrSize);

            return (new Tuple<object, String, int>(structObject, resultString, structSize));
        }


        //将struct，按从左至右的顺序填充到一个byte[]
        public static byte[] CombineStructsToBytes(params object[] structs)
        {
            //代码的实现方式是  从右到左填充 - -
            byte[] bytes = StructToBytes(structs[structs.Length-1]);
            for (int i = structs.Length - 2; i >= 0; --i)
            {
                bytes = VariableLengthStructToBytes(structs[i], bytes);
            }
            return bytes;
        }

        public static String NulltTerminatedAsciiBytesToString(byte[] bytes, int begin = -1, int count = -1)
        {
            String resultString;
            if (begin == -1 || count == -1)
            {
                resultString = System.Text.Encoding.ASCII.GetString(bytes);
            }
            else
            {
                resultString = System.Text.Encoding.ASCII.GetString(bytes, begin, count);
            }

            int index = resultString.IndexOf('\0');
            if (index >= 0)
            {
                resultString = resultString.Remove(index);
            }
            return resultString;
        }

        #endregion
    }
}
